package com.common.config.shiro;

import com.core.filter.JwtFilter;
import com.common.shiro.dao.DefaultRedisWebSessionManager;
import com.common.shiro.dao.RedisSessionDAO;
import com.common.shiro.realm.CustomRealm;

import java.util.*;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {



    @Bean(name = "simpleCookie")
    public SimpleCookie simpleCookie() {
        // 这个参数是 cookie 的名称，叫什么都行,我这块取名 rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("shiro_rememberMe_key");
        // setCookie 的 httponly 属性如果设为 true 的话，会增加对 xss 防护的安全系数，
        // 只能通过http访问，javaScript无法访问，防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        // LAX :浏览器允许在某些跨站请求中发送 Cookie，但仅限于安全的 GET 请求（如导航到页面）
        // 不配置的话，默认是NONE，，浏览器会在所有跨站请求中发送 cookie，但是此时会开启secure模式，cookie只能通过https传输
        simpleCookie.setSameSite(Cookie.SameSiteOptions.LAX);
        // 记住我 cookie 生效时间3天 ,单位是秒
        simpleCookie.setMaxAge(60 * 60 * 24 * 3);
        return simpleCookie;
    }

    /**
     * cookie管理对象;记住我功能,rememberMe管理器
     *
     * @return
     */
    @Bean(name = "cookieRememberMeManager")
    public RememberMeManager cookieRememberMeManager(@Qualifier("simpleCookie") SimpleCookie simpleCookie) {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(simpleCookie);
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }


    /**
     * 创建内存sessionDAO
     * @return
     */
//    @Bean(name = "memorySessionDAO")
//    public SessionDAO memorySessionDAO() {
//        return new MemorySessionDAO();
//    }

    /**
     * 创建redisSessionDAO
     * @return
     */
    @Bean(name = "redisSessionDAO")
    public SessionDAO redisSessionDAO() {
        return new RedisSessionDAO();
    }


    /**
     * 创建session管理器
     * @param redisSessionDAO
     * @return
     */
    @Bean(name = "defaultRedisWebSessionManager")
   public SessionManager defaultRedisWebSessionManager(@Qualifier("redisSessionDAO") SessionDAO redisSessionDAO) {
        DefaultRedisWebSessionManager defaultRedisWebSessionManager = new DefaultRedisWebSessionManager();
        defaultRedisWebSessionManager.setSessionDAO(redisSessionDAO);
        return defaultRedisWebSessionManager;
    }

    /**
     * 创建自定义的Realm
     *
     * @return 自定义的Realm
     */
    @Bean(name = "customRealm")
    public CustomRealm customRealm( ) {
        return new CustomRealm();
    }

    /**
     * 配置SecurityManager（核心管理器）关联自定义Realm
     */
    @Bean(name = "defaultWebSecurityManager")
    public SecurityManager defaultWebSecurityManager
    (   @Qualifier("customRealm") CustomRealm customRealm,
        @Qualifier("cookieRememberMeManager") RememberMeManager cookieRememberMeManager,
        @Qualifier("defaultRedisWebSessionManager") SessionManager defaultRedisWebSessionManager
    ) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 设置自定义Realm
        defaultWebSecurityManager.setRealm(customRealm);

        /**
        * 如果使用jwt，则不需要session维持登录状态了，可以关闭session功能
         * 比如记住我的功能，可以在前端实现，前端保存token即可
         */
//        // 设置记住我cookie存储管理器
//        defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager);
//        // 设置session管理器
//        defaultWebSecurityManager.setSessionManager(defaultRedisWebSessionManager);

    // 关闭shiro自带的session
    // 关闭shiroDao功能
    DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
    DefaultSessionStorageEvaluator defaultSessionStorageEvaluator =
            new DefaultSessionStorageEvaluator();
    // 不需要将ShiroSession中的东西存到任何地方包括Http Session中）
    defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
    subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
    defaultWebSecurityManager.setSubjectDAO(subjectDAO);

        // 返回安全管理器
        return defaultWebSecurityManager;
    }

    /**
     * 配置Shiro过滤器链
     */
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(
            @Qualifier("defaultWebSecurityManager") SecurityManager defaultWebSecurityManager) {

        // 创建过滤器工厂
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        // 设置安全管理器 SecurityManager
        factoryBean.setSecurityManager(defaultWebSecurityManager);

        // 注册自定义JWT过滤器
        factoryBean.getFilters().put("jwt", new JwtFilter(true));

        // 配置过滤规则
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        /**
         * anon : AnonymousFilter 进行拦截，底层是执行放行 authc : AuthenticationFilter 进行拦截，底层是执行认证
         *
         * <p>更多详情参考 DefaultFilter 这个枚举类
         */

        // 测试接口放行 请求前缀为 /test
        filterChainDefinitionMap.put("/test/**", "anon");

        // Swagger 相关路径放行
        filterChainDefinitionMap.put("/doc.html", "anon");           // Swagger文档页面
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");    // Swagger UI页面
        filterChainDefinitionMap.put("/swagger-resources/**", "anon"); // Swagger资源
        filterChainDefinitionMap.put("/v2/api-docs", "anon");        // API文档
        filterChainDefinitionMap.put("/v2/api-docs-ext", "anon"); // Knife4j 扩展接口
        filterChainDefinitionMap.put("/v3/api-docs", "anon");        // OpenAPI 3.0文档
        filterChainDefinitionMap.put("/webjars/**", "anon");         // Swagger静态资源
        filterChainDefinitionMap.put("/configuration/ui", "anon");   // Swagger配置
        filterChainDefinitionMap.put("/configuration/security", "anon"); // Swagger安全配置


        // 应用接口放行
        filterChainDefinitionMap.put("/login", "anon"); // 登录接口匿名访问
        filterChainDefinitionMap.put("/logout", "anon"); // 登出接口匿名访问
        filterChainDefinitionMap.put("/register", "anon"); // 注册接口匿名访问
        filterChainDefinitionMap.put("/getVerifyCode", "anon"); // 获取验证码接口匿名访问
        filterChainDefinitionMap.put("/static/**", "anon"); // 静态资源匿名访问
        filterChainDefinitionMap.put("/favicon.ico", "anon"); // 网站图标

        // 记住我接口放行(使用UserFilter过滤器，如果登录过了，这个会在浏览器中生成一个cookie，服务器保存信息)
        filterChainDefinitionMap.put("/user/rememberMe", "user");
        filterChainDefinitionMap.put("/user/authcPage", "authcBasic");

        // 健康检查接口（如果需要）
        filterChainDefinitionMap.put("/actuator/health", "anon");
        filterChainDefinitionMap.put("/health", "anon");

        // 其他接口需JWT认证
        filterChainDefinitionMap.put("/**", "jwt");

        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return factoryBean;
    }

    /**
     * 交由 Spring 来自动地管理 Shiro-Bean 的生命周期
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
        app.setProxyTargetClass(true);
        return app;
    }

    /**
     * 为 Spring-Bean 开启对 Shiro 注解的支持 启用Shiro注解支持（如@RequiresRoles、@RequiresPermissions）
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("defaultWebSecurityManager") SecurityManager defaultWebSecurityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(defaultWebSecurityManager);
        return advisor;
    }
}